package com.primeton.eos.oss.model.service;


import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.eos.spring.dao.CriteriaBuilder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.primeton.eos.api.springboot.EOS8ApplicationContext;
import com.primeton.eos.oss.model.dao.ProjectFileDao;
import com.primeton.eos.oss.model.newdataset.CrecSysOrganization;
import com.primeton.eos.oss.model.newdataset.CrecSysOrganizationHis;
import com.primeton.eos.oss.model.newdataset.ProjectFile;
import com.primeton.eos.oss.model.newdataset.impl.ProjectFileImpl;
import com.primeton.eos.oss.model.result.BusinessException;
import com.primeton.eos.oss.model.result.ErrorResult;
import com.primeton.eos.oss.model.result.RetCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.primeton.eos.oss.model.dto.ProjectFileDto;


@Service
public class FileService {

    @Autowired
    ProjectFileDao projectFileDao;

    private static final Logger log = LoggerFactory.getLogger(FileService.class);

	public void loadWrite(InputStream inputStream) throws IOException {
		// IO读取并进行解压进行解压
        // 存放解压目录位置
        String extractRootPath= EOS8ApplicationContext.INSTANCE.getProperty("file.extract.path");
        // 缓存文件名称
        Map<String,String> cacheMap=new HashMap();

        unzip(inputStream,extractRootPath,cacheMap);
        // 解析 *.txt，建立 项目ID 和文件映射关联关系
        List<ProjectFileDto> projectFileDtos = new ArrayList<>();
        // 定义模型编码和模型名称的映射关系
        Map<String, String> moduleMap=new HashMap<>();
        Path targetDir = Paths.get(extractRootPath, cacheMap.get("fileName").replace(".zip", ""));
        parseDirectory(targetDir.toString(), projectFileDtos,moduleMap);
        // 进行批量数据落库
        Date date=new Date();
        CompletableFuture.runAsync(() -> {
                    synchronized (this) {
                        for (ProjectFileDto projectFileDto : projectFileDtos) {
                            ProjectFile projectFile = new ProjectFileImpl();
                            if (!moduleMap.isEmpty() && StrUtil.isNotBlank(moduleMap.get(projectFileDto.getModule()))) {
                                projectFile.setModuleName(moduleMap.get(projectFileDto.getModule()));
                            }
                            projectFile.setFileNames(projectFileDto.getFileName());
                            projectFile.setFilePath(projectFileDto.getFilePath());
                            projectFile.setDelFlag("N");
                            projectFile.setFsProNumber(projectFileDto.getFsProNumber());
                            projectFile.setBtnTime(DateUtil.format(date, DatePattern.NORM_DATETIME_FORMAT));
                            projectFile.setInsertdate(new Date());
                            projectFile.setUpdatedate(new Date());
                            projectFile.setProjectid(projectFileDto.getId());
                            projectFile.setModule(projectFileDto.getModule());
                            CriteriaBuilder cb = new CriteriaBuilder(ProjectFile.QNAME);
                            cb.and("projectid", projectFile.getProjectid());
                            cb.and("del_flag", "N");
                            List<ProjectFile> datas = projectFileDao.queryEntitiesByCriteria(cb.build());
                            if (!CollectionUtil.isEmpty(datas)) {
                                datas.stream().forEach(item -> {
                                    item.setDelFlag("Y");
                                    item.setUpdatedate(new Date()); // 设置当前时间
                                });
                                projectFileDao.updateEntity(datas);
                                ;
                            }
                            projectFileDao.getPrimaryKey(projectFile);
                            projectFileDao.insertEntity(projectFile);
                        }
                    }
        });

	}

    // 辅助方法：从 InputStream 获取 ZIP 文件名
    private String getZipFileName(InputStream inputStream) throws IOException {
        try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
            ZipEntry entry = zipInputStream.getNextEntry();
            if (entry != null) {
                return entry.getName(); // 返回第一个条目的名称
            }
        }
        throw new IOException("Cannot determine ZIP file name from input stream.");
    }

    /**
     *
     * @param inputStream IO读取
     * @param destDir 解压存放目录
     * @throws IOException
     */
    private static void unzip(InputStream inputStream,String destDir,Map<String,String> cacheMap) throws IOException {
        try  {
            String extractEncoding= EOS8ApplicationContext.INSTANCE.getProperty("file.extract.encoding");
            Charset encoding = Charset.forName(extractEncoding);
            ZipInputStream zipInputStream = new ZipInputStream(inputStream,encoding);
            ZipEntry entry;
            while ((entry = zipInputStream.getNextEntry()) != null) {
                if (StrUtil.isBlank(cacheMap.get("fileName"))){
                    cacheMap.put("fileName",entry.getName());
                }
                File file = new File(destDir, entry.getName());
                if (entry.isDirectory()) {
                    file.mkdirs();
                } else {
                    file.getParentFile().mkdirs();
                    try (OutputStream outputStream = new FileOutputStream(file)) {
                        byte[] buffer = new byte[1024];
                        int bytesRead;
                        while ((bytesRead = zipInputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                    }
                }
                zipInputStream.closeEntry();
            }
        }catch (Exception ex){
            log.info("FileService.unzip is error>>>>>>>>>>"+ex);
        }
    }

    private static void parseDirectory(String dirPath, List<ProjectFileDto> projectFileDtos, Map<String, String> moduleMap) throws IOException {
        Files.walk(Paths.get(dirPath)).forEach(path -> {
            if (Files.isRegularFile(path)) {
                // 解析TXT，获取相关业务数据进行组装
                if (path.getFileName().toString().endsWith(".txt")) {
                    handleDataFile(dirPath,path, projectFileDtos,moduleMap);
                }
            } else if (Files.isDirectory(path)) {
                // 非TXT 并且是文件进行业务数据建立映射关联关系
                String parentId = getParentIdFromDirectory(path);
                if (StrUtil.isNotBlank(parentId) && isNumeric(parentId)) {
                    collectFilesUnderDirectory(path, parentId, projectFileDtos);
                }
            }
        });
    }

    private static String getParentIdFromDirectory(Path directory) {
        try {
            return directory.getFileName().toString();
        } catch (NumberFormatException e) {
            return null; // 不能解析为 ID 的情况
        }
    }

    private static void collectFilesUnderDirectory(Path directory, String parentId, List<ProjectFileDto> projectFileDtos) {
        StringBuilder fileNames = new StringBuilder();
        try {
            // 收集该目录下的所有文件名
            Files.list(directory).forEach(filePath -> {
                if (Files.isRegularFile(filePath)) {
                    if (fileNames.length() > 0) {
                        fileNames.append(";"); // 多个文件用分号隔开
                    }
                    fileNames.append(filePath.getFileName().toString());
                }
            });

            // 查找对应 parentId 的 ProjectFileDto
            for (ProjectFileDto dto : projectFileDtos) {
                if (dto.getId().equals(parentId)) { // 假设 ProjectFileDto 有 getId() 方法
                    dto.setFileName(fileNames.toString()); // 假设有 setFileNames() 方法来更新文件名
                    break; // 找到后可以退出循环
                }
            }
        } catch (IOException ex) {
            log.info("FileService.collectFilesUnderDirectory is error>>>>>>>>>>"+ex);
            ex.printStackTrace();
        }
    }

    private static void handleDataFile(String rootPath,Path dataFilePath, List<ProjectFileDto> projectFileDtos, Map<String, String> moduleMap) {
        try {
            // 文档解析编码 一般和ZIP压缩格式是一样，此处可独立配置
            String extractEncoding= EOS8ApplicationContext.INSTANCE.getProperty("file.extract.txt.encoding");
            String jsonData = new String(Files.readAllBytes(dataFilePath), Charset.forName(extractEncoding));
            parseJson(rootPath,jsonData, projectFileDtos,moduleMap);
        } catch (IOException ex) {
            log.info("FileService.handleDataFile is error>>>>>>>>>>"+ex);
        }
    }

    private static void parseJson(String rootPath,String jsonData, List<ProjectFileDto> projectFileDtos, Map<String, String> moduleMap) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode rootNode = objectMapper.readTree(jsonData);
            JsonNode dataArray = rootNode.path("data");
            JsonNode moduleNode = rootNode.path("description").path("module");
            for (JsonNode item : dataArray) {
                String id = item.path("id").asText();;
                //    // 不提供
//                String module = item.path("module").asText();
                // 不提供
//                String fsProNumber = item.path("fsProNumber").asText();
                String annex = item.path("annex").asText();
                String module = annex.split("/")[0];
                // 取父级目录
                String filePath = rootPath+"/"+annex;
                // 找到与该 ID 相关的文件名
                // 这里需要根据项目 ID 关联文件名，可以调整逻辑
                projectFileDtos.add(new ProjectFileDto(id, module, item.path("yearNo").asText(), "",filePath));
            }
            // 只需要解析一次即可
            if (moduleMap.isEmpty()){
                moduleMap.putAll(objectMapper.convertValue(moduleNode, Map.class));
            }
        } catch (IOException ex) {
            log.info("FileService.handleDataFile is error>>>>>>>>>>"+ex);
        }
    }

    private static boolean isNumeric(String str) {
        if (str == null || str.isEmpty()) {
            return false;
        }
        for (char c : str.toCharArray()) {
            if (!Character.isDigit(c)) {
                return false;
            }
        }
        return true;
    }

    public void readFile(String projectid) {
        // 通过项目ID获取其附件信息
        CriteriaBuilder cb = new CriteriaBuilder(ProjectFile.QNAME);
        cb.and("projectid", projectid);
        cb.and("del_flag", "N");
        List<ProjectFile> datas = projectFileDao.queryEntitiesByCriteria(cb.build());
        if (CollectionUtil.isEmpty(datas)){
            throw new BusinessException(RetCode.FAILE,"此项目暂无查询到附件信息");
        }
        // 取其中一条的相对路径 存放规则filePath+项目ID+附件
        ProjectFile projectFile=datas.get(0);
        String path=projectFile.getFilePath()+"/"+projectid+"/";
        // 所有附件的绝对路径
        List<String> files=new ArrayList<>();
        for (String str:projectFile.getFilePath().split(";")){
            files.add(path+str);
        }
        // 将files进行ZIP压缩 然后名称打包成  projectFile.getProjectid().zip格式进行输出
        // ZIP文件的目标路径
        String zipFileName = projectFile.getProjectid() + ".zip";
        try (FileOutputStream fos = new FileOutputStream(zipFileName);
             ZipOutputStream zipOut = new ZipOutputStream(fos)) {

            for (String filePath : files) {
                File fileToZip = new File(filePath);
                if (fileToZip.exists()) {
                    zipFile(fileToZip, zipOut);
                }
            }
        } catch (IOException ex) {
            log.info("readFile  creating ZIP file is error:"+ex);
            throw new BusinessException(RetCode.FAILE, "Error while creating ZIP file");
        }

    }

    private void zipFile(File fileToZip, ZipOutputStream zipOut) throws IOException {
        try (FileInputStream fis = new FileInputStream(fileToZip)) {
            ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
            zipOut.putNextEntry(zipEntry);
            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {
                zipOut.write(bytes, 0, length);
            }
            zipOut.closeEntry();
        }
    }

}
